/*
 * Copyright 2009 XueSong Guo.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package cn.webwheel.el;

import cn.webwheel.compiler.JavaC;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.text.DecimalFormat;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class ExpParser {

    private static Pattern patStr = Pattern.compile("'.*?'");
    private static Pattern patNum = Pattern.compile("^-?[0-9]+\\.[0-9]+[dDfF]?|^-?[0-9]+[lL]?");
    private static Pattern patId = Pattern.compile("^[a-zA-Z\\$_][a-zA-Z0-9\\$_]*");
    private static String[] ops = new String[] {"*", "/", "%", "+", "-", "<=", "<", ">=", ">", "==", "!=", "&&", "||", "?", ":"};

    private Stack<Var> comContexts;
    private Stack<VarContext> varContexts;

    private Map<String, String> strMap;

    public ExpParser(Stack<Var> comContexts, Stack<VarContext> varContexts) {
        this.comContexts = comContexts;
        this.varContexts = varContexts;
    }

    public Expression parse(String exp) throws ParseException {
        if (strMap != null) {
            throw new IllegalStateException();
        }
        strMap = new HashMap<String, String>();
        int strIdx = 0;
        DecimalFormat df = new DecimalFormat("00000");

        StringBuilder sb = new StringBuilder();
        Matcher matcher = patStr.matcher(exp);

        int bi = 0, ei;
        while (matcher.find()) {
            ei = matcher.start();
            if (bi != ei) sb.append(exp.substring(bi, ei));
            String s = matcher.group();
            s = s.substring(1, s.length() - 1).replace("&quot;", "\\\"").replace("&apos;", "'").replace("&lt;", "<").replace("&gt;", ">").replace("&#125;", "}").replace("&amp;", "&");
            String k = "_sv_" + df.format(strIdx++);
            strMap.put(k, s);
            sb.append(k);
            bi = matcher.end();
        }
        ei = exp.length();
        if (bi != ei) sb.append(exp.substring(bi, ei));

        Expression e = parseExp(sb.toString().replace("&lt;", "<").replace("&gt;", ">").replace("&amp;", "&"));
        e.setOriginalString(exp);
        return e;
    }

    private static class ExpUnit {
        Expression exp = new Expression();
        String left;
    }

    private static class OpUnit {
        Operator op;
        String left;
    }

    private Expression parseExp(String exp) throws ParseException {
        exp = exp.trim();
        List<Object> list = new ArrayList<Object>();
        for(boolean waitExp = true; exp.length()!=0; exp = exp.trim(), waitExp = !waitExp) {
            if (waitExp) {
                ExpUnit unit = parseExpUnit(exp);
                list.add(unit.exp);
                exp = unit.left;
            } else {
                OpUnit unit = parseOpUnit(exp);
                list.add(unit.op);
                exp = unit.left;
            }
        }
        if(list.isEmpty() || !(list.get(list.size() - 1) instanceof Expression)) throw new ParseException();
        return parseExp(list);
    }

    private Expression parseExp(List<Object> list) throws ParseException {
        if(list.size()==1) return (Expression) list.get(0);

        //check ? :
        for(int i=0; i<list.size(); i++) {
            Object o = list.get(i);
            if(!(o instanceof Operator)) continue;
            Operator op = (Operator) o;
            if(op.name.equals("?")) {
                Expression type = parseExp(list.subList(0, i));
                if(type.getReturnType().toClass() != Boolean.TYPE && type.getReturnType().toClass() != Boolean.class) throw new ParseException();
                int p = 1;
                for(int j=i+2; j<list.size(); j++) {
                    if(!(list.get(j) instanceof Operator)) continue;
                    String n = ((Operator)list.get(j)).name;
                    if(n.equals("?")) p++;
                    else if(n.equals(":")) {
                        if(--p==0) {
                            Expression type2 = parseExp(list.subList(i+1, j));
                            Expression type3 = parseExp(list.subList(j+1, list.size()));
                            Expression ei = new Expression();
                            if(type2.getReturnType().isAssignableFrom(type3.getReturnType())) {
                                ei.setReturnType(type2.getReturnType());
                            } else if(type3.getReturnType().isAssignableFrom(type2.getReturnType())) {
                                ei.setReturnType(type3.getReturnType());
                            } else {
                                throw new ParseException();
                            }
                            if (JavaC.javassist) {
                                if (!type.getReturnType().isPrimitive()) {
                                    ei.setEvalString(type.getEvalString() + ".booleanValue() ? " + type2.getEvalString() + " : " + type3.getEvalString());
                                } else {
                                    ei.setEvalString(type.getEvalString() + " ? " + type2.getEvalString() + " : " + type3.getEvalString());
                                }
                            } else {
                                ei.setEvalString(type.getEvalString() + " ? " + type2.getEvalString() + " : " + type3.getEvalString());
                            }
                            return ei;
                        }
                    }
                }
                throw new ParseException();
            }
        }

        //check ||
        for(int i=0; i<list.size(); i++) {
            Object o = list.get(i);
            if(!(o instanceof Operator)) continue;
            Operator op = (Operator) o;
            if(op.name.equals("||")) {
                Expression type = parseExp(list.subList(0, i));
                if(type.getReturnType().toClass() != Boolean.TYPE && type.getReturnType().toClass() != Boolean.class) throw new ParseException();
                Expression type2 = parseExp(list.subList(i+1, list.size()));
                if(type2.getReturnType().toClass() != Boolean.TYPE && type2.getReturnType().toClass() != Boolean.class) throw new ParseException();
                Expression ei = new Expression();
                ei.setReturnType(new GenericClass(Boolean.TYPE));
                if (JavaC.javassist) {
                    ei.setEvalString((type.getReturnType().isPrimitive() ? type.getEvalString() : GenericClass.toPrimitive(type.getReturnType().toClass(), type.getEvalString())) + " || "
                            + (type2.getReturnType().isPrimitive() ? type2.getEvalString() : GenericClass.toPrimitive(type2.getReturnType().toClass(), type2.getEvalString())));
                } else {
                    ei.setEvalString(type.getEvalString() + " || " + type2.getEvalString());
                }
                return ei;
            }
        }

        //check &&
        for(int i=0; i<list.size(); i++) {
            Object o = list.get(i);
            if(!(o instanceof Operator)) continue;
            Operator op = (Operator) o;
            if(op.name.equals("&&")) {
                Expression type = parseExp(list.subList(0, i));
                if(type.getReturnType().toClass() != Boolean.TYPE && type.getReturnType().toClass() != Boolean.class) throw new ParseException();
                Expression type2 = parseExp(list.subList(i+1, list.size()));
                if(type2.getReturnType().toClass() != Boolean.TYPE && type2.getReturnType().toClass() != Boolean.class) throw new ParseException();
                Expression ei = new Expression();
                ei.setReturnType(new GenericClass(Boolean.TYPE));
                if (JavaC.javassist) {
                    ei.setEvalString((type.getReturnType().isPrimitive() ? type.getEvalString() : GenericClass.toPrimitive(type.getReturnType().toClass(), type.getEvalString())) + " && "
                            + (type2.getReturnType().isPrimitive() ? type2.getEvalString() : GenericClass.toPrimitive(type2.getReturnType().toClass(), type2.getEvalString())));
                } else {
                    ei.setEvalString(type.getEvalString() + " && " + type2.getEvalString());
                }
                return ei;
            }
        }

        //check == !=
        for(int i=0; i<list.size(); i++) {
            Object o = list.get(i);
            if(!(o instanceof Operator)) continue;
            Operator op = (Operator) o;
            if(op.name.equals("==") || op.name.equals("!=")) {
                Expression type = parseExp(list.subList(0, i));
                Expression type2 = parseExp(list.subList(i+1, list.size()));
                if(!GenericClass.canEquals(type.getReturnType(), type2.getReturnType())) throw new ParseException();
                Expression ei = new Expression();
                ei.setReturnType(new GenericClass(Boolean.TYPE));
                if (JavaC.javassist) {
                    if (type.getReturnType().isPrimitive() && !type2.getReturnType().isPrimitive()) {
                        ei.setEvalString(type.getEvalString() + " " + op.name + " " + GenericClass.toPrimitive(type2.getReturnType().toClass(), type2.getEvalString()));
                    } else if (!type.getReturnType().isPrimitive() && type2.getReturnType().isPrimitive()) {
                        ei.setEvalString(GenericClass.toPrimitive(type.getReturnType().toClass(), type.getEvalString()) + " " + op.name + " " + type2.getEvalString());
                    } else {
                        ei.setEvalString(type.getEvalString() + " " + op.name + " " + type2.getEvalString());
                    }
                } else {
                    ei.setEvalString(type.getEvalString() + " " + op.name + " " + type2.getEvalString());
                }
                return ei;
            }
        }

        //check < > <= >=
        for(int i=0; i<list.size(); i++) {
            Object o = list.get(i);
            if(!(o instanceof Operator)) continue;
            Operator op = (Operator) o;
            if(op.name.equals(">") || op.name.equals("<") || op.name.equals("<=") || op.name.equals(">=")) {
                Expression type = parseExp(list.subList(0, i));
                Expression type2 = parseExp(list.subList(i+1, list.size()));
                if(!GenericClass.canCompare(type.getReturnType(), type2.getReturnType())) throw new ParseException();
                Expression ei = new Expression();
                ei.setReturnType(new GenericClass(Boolean.TYPE));
                if (JavaC.javassist) {
                    ei.setEvalString((type.getReturnType().isPrimitive() ? type.getEvalString() : GenericClass.toPrimitive(type.getReturnType().toClass(), type.getEvalString())) + " " + op.name + " "
                            + (type2.getReturnType().isPrimitive() ? type2.getEvalString() : GenericClass.toPrimitive(type2.getReturnType().toClass(), type2.getEvalString())));
                } else {
                    ei.setEvalString(type.getEvalString() + " " + op.name + " " + type2.getEvalString());
                }
                return ei;
            }
        }

        //check + -
        for(int i=0; i<list.size(); i++) {
            Object o = list.get(i);
            if(!(o instanceof Operator)) continue;
            Operator op = (Operator) o;
            if(op.name.equals("+") || op.name.equals("-")) {
                Expression type = parseExp(list.subList(0, i));
                Expression type2 = parseExp(list.subList(i+1, list.size()));
                Expression ei = new Expression();
                GenericClass t;
                if(op.name.equals("-")) {
                    t = GenericClass.canSub(type.getReturnType(), type2.getReturnType());
                } else {
                    t = GenericClass.canAdd(type.getReturnType(), type2.getReturnType());
                }
                if(t==null) throw new ParseException();
                ei.setReturnType(t);
                if (JavaC.javassist) {
                    ei.setEvalString((type.getReturnType().isPrimitive() ? type.getEvalString() : GenericClass.toPrimitive(type.getReturnType().toClass(), type.getEvalString())) + " " + op.name + " "
                            + (type2.getReturnType().isPrimitive() ? type2.getEvalString() : GenericClass.toPrimitive(type2.getReturnType().toClass(), type2.getEvalString())));
                } else {
                    ei.setEvalString(type.getEvalString() + " " + op.name + " " + type2.getEvalString());
                }
                return ei;
            }
        }

        //check * / %
        for(int i=0; i<list.size(); i++) {
            Object o = list.get(i);
            if(!(o instanceof Operator)) continue;
            Operator op = (Operator) o;
            if(op.name.equals("*") || op.name.equals("/") || op.name.equals("%")) {
                Expression type = parseExp(list.subList(0, i));
                Expression type2 = parseExp(list.subList(i+1, list.size()));
                Expression ei = new Expression();

                GenericClass t = GenericClass.canSub(type.getReturnType(), type2.getReturnType());
                if(t==null) throw new ParseException();

                if(op.name.equals("%")) {
                    if (t.toClass() == Double.TYPE || t.toClass() == Float.TYPE) {
                        throw new ParseException();
                    }
                }

                ei.setReturnType(t);
                if (JavaC.javassist) {
                    ei.setEvalString((type.getReturnType().isPrimitive() ? type.getEvalString() : GenericClass.toPrimitive(type.getReturnType().toClass(), type.getEvalString())) + " " + op.name + " "
                            + (type2.getReturnType().isPrimitive() ? type2.getEvalString() : GenericClass.toPrimitive(type2.getReturnType().toClass(), type2.getEvalString())));
                } else {
                    ei.setEvalString(type.getEvalString() + " " + op.name + " " + type2.getEvalString());
                }
                return ei;
            }
        }

        throw new ParseException();
    }

    private int lookForPair(String exp) throws ParseException {
        char close;
        char open = exp.charAt(0);
        if(open=='(') close = ')';
        else if(open=='[') close = ']';
        else throw new ParseException();
        int p = 1;
        for (int i = 1; i < exp.length(); i++) {
            char c = exp.charAt(i);
            if(c==open) p++;
            else if(c==close) {
                if(--p==0) return i;
            }
        }
        throw new ParseException();
    }

    private ExpUnit parseExpUnit(String exp) throws ParseException {

        exp = exp.trim();

        if (exp.startsWith("!")) {
            ExpUnit unit = parseExpUnit(exp.substring(1));
            Class<?> aClass = unit.exp.getReturnType().toClass();
            if (aClass != boolean.class && aClass != Boolean.class) {
                throw new ParseException("! with non boolean type");
            }
            unit.exp.setEvalString("!" + unit.exp.getEvalString());
            return unit;
        }

        if (exp.startsWith("+") || exp.startsWith("-")) {
            char c = exp.charAt(0);
            exp = exp.substring(1);
            if (exp.startsWith("+") || exp.startsWith("-")) {
                throw new ParseException("many signs");
            }
            ExpUnit unit = parseExpUnit(exp);
            Class<?> cls = unit.exp.getReturnType().toClass();
            if (cls != byte.class && cls != Byte.class &&
                    cls != short.class && cls != Short.class &&
                    cls != int.class && cls != Integer.class &&
                    cls != long.class && cls != Long.class &&
                    cls != float.class && cls != Float.class &&
                    cls != double.class && cls != Double.class) {
                throw new ParseException("invalid sign");
            }
            unit.exp.setEvalString(c + unit.exp.getEvalString());
            return unit;
        }

        ExpUnit unit = null;

        if(exp.startsWith("(")) {
            int pi = lookForPair(exp);
            unit = new ExpUnit();
            unit.exp = parseExp(exp.substring(1, pi));
            unit.exp.setEvalString('(' + unit.exp.getEvalString() + ')');
            unit.left = exp.substring(pi+1);
        }

        if(unit==null && exp.startsWith("_sv_")) {
            String s = strMap.get(exp.substring(0, 9));
            if(s==null) throw new ParseException();
            unit = new ExpUnit();
            unit.exp.setReturnType(new GenericClass(String.class));
            unit.exp.setEvalString("\"" + s + "\"");
            unit.left = exp.substring(9);
        }

        if(unit==null) {
            Matcher matcher = patNum.matcher(exp);
            if(matcher.find()) {
                boolean goon = false;
                String s = matcher.group();
                unit = new ExpUnit();
                unit.left = exp.substring(matcher.end());
                unit.exp.setEvalString(s);
                char c = s.charAt(s.length() - 1);
                if(c=='f' || c=='F') {
                    try {
                        Float.parseFloat(s.substring(0, s.length() - 1));
                    } catch (NumberFormatException e) {
                        throw new ParseException(e);
                    }
                    unit.exp.setReturnType(new GenericClass(Float.TYPE));
                } else if (c == 'd' || c == 'D' || s.indexOf('.') != -1) {
                    try {
                        if (c == 'd' || c == 'D') s = s.substring(0, s.length() - 1);
                        Double.parseDouble(s);
                    } catch (NumberFormatException e) {
                        throw new ParseException(e);
                    }
                    unit.exp.setReturnType(new GenericClass(Double.TYPE));
                } else if (c == 'l' || c == 'L') {
                    try {
                        Long.parseLong(s.substring(0, s.length() - 1));
                    } catch (NumberFormatException e) {
                        throw new ParseException(e);
                    }
                    unit.exp.setReturnType(new GenericClass(Long.TYPE));
                } else {
                    int d;
                    try {
                        d = Integer.parseInt(s);
                    } catch (NumberFormatException e) {
                        throw new ParseException(e);
                    }
                    if (d > 0) {
                        Var var = findVar(s);
                        if (var != null) {
                            unit.exp.setEvalString(var.getVar());
                            unit.exp.setReturnType(var.getType());
                            goon = true;
                        }
                    }
                    if(!goon) {
                        Class t;
                        if(d>-129 && d<128) t = Byte.TYPE;
                        else if(d>-32769 && d<32768) t = Short.TYPE;
                        else t = Integer.TYPE;
                        unit.exp.setReturnType(new GenericClass(t));
                    }
                }
                if(!goon) {
                    return unit;
                }
            }
        }

        if(unit==null) {
            Matcher matcher = patId.matcher(exp);
            if(!matcher.find()) throw new ParseException();

            String s = matcher.group();

            if("true".equals(s) || "false".equals(s)) {
                unit = new ExpUnit();
                unit.exp.setEvalString(s);
                unit.exp.setReturnType(new GenericClass(Boolean.TYPE));
                unit.left = exp.substring(matcher.end());
                return unit;
            } else if("null".equals(s)) {
                unit = new ExpUnit();
                unit.exp.setEvalString("null");
                unit.exp.setReturnType(new GenericClass(null));
                unit.left = exp.substring(matcher.end());
                return unit;
            }

            Var var = findVar(s);
            if (var != null) {
                unit = new ExpUnit();
                unit.left = exp.substring(matcher.end());
                unit.exp.setEvalString(var.getVar());
                unit.exp.setReturnType(var.getType());
            } else {
                if (comContexts.isEmpty()) {
                    throw new ParseException();
                }
                for (int i = comContexts.size() - 1; i >= 0; i--) {
                    try {
                        Var ctx = comContexts.get(i);
                        unit = parseOneMethod(exp, ctx.getType());
                        if (JavaC.javassist) {
                            unit.exp.setEvalString("((" + unit.exp.getReturnType() + ")" + ctx.getVar() + unit.exp.getEvalString() + ")");
                        } else {
                            unit.exp.setEvalString(ctx.getVar() + unit.exp.getEvalString());
                        }
                        break;
                    } catch (ParseException e) {
                        if (i == 0) throw e;
                    }
                }
            }
        }

        exp = unit.left;
        for(; exp.length()!=0 && exp.charAt(0)=='.'; exp = exp.trim()) {
            ExpUnit eu = parseOneMethod(exp.substring(1), unit.exp.getReturnType());
            if (JavaC.javassist) {
                unit.exp.setEvalString("((" + eu.exp.getReturnType() + ")" + unit.exp.getEvalString() + eu.exp.getEvalString() + ")");
            } else {
                unit.exp.setEvalString(unit.exp.getEvalString() + eu.exp.getEvalString());
            }
            unit.exp.setReturnType(eu.exp.getReturnType());
            exp = eu.left;
        }
        unit.left = exp;
        return unit;
    }

    private Var findVar(String name) {
        for (int i = varContexts.size() - 1; i >= 0; i--) {
            VarContext c = varContexts.get(i);
            Var var = c.getVar(name);
            if(var!=null) return var;
        }
        return null;
    }

    private OpUnit parseOpUnit(String exp) throws ParseException {
        exp = exp.trim();
        for (String op : ops) {
            if(exp.startsWith(op)) {
                OpUnit unit = new OpUnit();
                unit.left = exp.substring(op.length());
                unit.op = new Operator(op);
                return unit;
            }
        }
        throw new ParseException();
    }

    private ExpUnit parseOneMethod(String exp, GenericClass ctx) throws ParseException {
        exp = exp.trim();
        Matcher matcher = patId.matcher(exp);
        if(!matcher.find()) throw new ParseException();
        exp = exp.substring(matcher.end()).trim();
        String s = matcher.group();
        List<Expression> params = new ArrayList<Expression>();
        boolean isMethod = false;
        if (exp.startsWith("(")) {
            isMethod = true;
            int idx = lookForPair(exp);
            StringBuilder sb = new StringBuilder(exp.substring(1, idx).trim());
            if (sb.length()>0) {
                for (int i = 0, p=0; i < sb.length(); i++) {
                    char c = sb.charAt(i);
                    if(c=='(') p++;
                    else if(c==')') p--;
                    else if(p!=0 && c==',') sb.setCharAt(i, ';');
                }
                for (String p : sb.toString().split(",")) {
                    params.add(parseExp(p.replace(';', ',')));
                }
            }
            exp = exp.substring(idx + 1).trim();
        }
        List<Expression> indices = new ArrayList<Expression>();
        while (exp.startsWith("[")) {
            int idx = lookForPair(exp);
            Expression e = parseExp(exp.substring(1, idx));
            if(!new GenericClass(Integer.TYPE).isAssignableFrom(e.getReturnType())) throw new ParseException();
            indices.add(e);
            exp = exp.substring(idx + 1).trim();
        }

        ExpUnit unit = new ExpUnit();
        unit.left = exp;

        Method getter = null;
        String hs = s.substring(0, 1).toUpperCase() + s.substring(1);
        loop:
        for (Method method : ctx.toClass().getMethods()) {
            String mn = method.getName();
            if (!mn.equals(s) && !mn.equals("get" + hs) && !(method.getReturnType() == Boolean.TYPE && mn.equals("is" + hs))) continue;
            Type[] pts = method.getGenericParameterTypes();
            if (pts.length != params.size()) continue;
            for (int i = 0; i < pts.length; i++) {
                if(!ctx.infer(pts[i]).isAssignableFrom(params.get(i).getReturnType())) continue loop;
            }
            if (getter != null) {
                getter = safeOverload(getter, method);
                if (getter == null) throw new ParseException();
            } else {
                getter = method;
            }
        }
        if (getter == null && !isMethod && indices.isEmpty()) {
            for (Field field : ctx.toClass().getFields()) {
                if (field.getName().equals(s)) {
                    unit.exp.setReturnType(ctx.infer(field.getGenericType()));
                    unit.exp.setEvalString("." + s);
                    return unit;
                }
            }
        }
        if (getter == null || getter.getReturnType()==Void.TYPE) throw new ParseException();
        if(!Modifier.isPublic(getter.getReturnType().getModifiers())) throw new ParseException();
        StringBuilder sb = new StringBuilder("." + getter.getName() + "(");
        for(int i=0; i<params.size(); i++) {
            Expression param = params.get(i);
            sb.append('(').append(getter.getParameterTypes()[i].getCanonicalName()).append(')');
            if (JavaC.javassist) {
                if (getter.getParameterTypes()[i].isPrimitive() && !param.getReturnType().isPrimitive()) {
                    sb.append(GenericClass.toPrimitive(param.getReturnType().toClass(), param.getEvalString())).append(',');
                } else if (!getter.getParameterTypes()[i].isPrimitive() && param.getReturnType().isPrimitive()) {
                    sb.append("($w)").append(param.getEvalString()).append(',');
                } else {
                    sb.append(param.getEvalString()).append(',');
                }
            } else {
                sb.append(param.getEvalString()).append(',');
            }
        }
        if (params.isEmpty()) {
            sb.append(')');
        } else {
            sb.setCharAt(sb.length() - 1, ')');
        }

        GenericClass rt = ctx.infer(getter.getGenericReturnType());
        for (Expression indice : indices) {
            if (!rt.toClass().isArray()) {
                throw new ParseException();
            }
            sb.append('[').append(indice.getEvalString()).append(']');
            rt = rt.getComponentType();
        }
        unit.exp.setReturnType(rt);
        unit.exp.setEvalString(sb.toString());
        return unit;
    }

    private Method safeOverload(Method m1, Method m2) {
        Class<?>[] pts1 = m1.getParameterTypes();
        Class<?>[] pts2 = m2.getParameterTypes();
        if (Arrays.equals(pts1, pts2)) {
            if (m1.getReturnType().isAssignableFrom(m2.getReturnType())) {
                return m2;
            } else {
                return m1;
            }
        }
        boolean overload = true;
        for (int i = 0; i < pts1.length; i++) {
            if (!pts1[i].isAssignableFrom(pts2[i])) {
                overload = false;
                break;
            }
        }
        if(overload) return m2;
        overload = true;
        for (int i = 0; i < pts1.length; i++) {
            if (!pts2[i].isAssignableFrom(pts1[i])) {
                overload = false;
                break;
            }
        }
        if(overload) return m1;
        return null;
    }
}
